/*******************************************************************************
 * Copyright (c) 2000, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.gef.examples.logicdesigner;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.parts.ScrollableThumbnail;
import org.eclipse.draw2d.parts.Thumbnail;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.dnd.TemplateTransferDragSourceListener;
import org.eclipse.gef.dnd.TemplateTransferDropTargetListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.examples.logicdesigner.actions.IncrementDecrementAction;
import org.eclipse.gef.examples.logicdesigner.actions.LogicPasteTemplateAction;
import org.eclipse.gef.examples.logicdesigner.dnd.TextTransferDropTargetListener;
import org.eclipse.gef.examples.logicdesigner.edit.GraphicalPartFactory;
import org.eclipse.gef.examples.logicdesigner.edit.TreePartFactory;
import org.eclipse.gef.examples.logicdesigner.model.LogicDiagram;
import org.eclipse.gef.examples.logicdesigner.model.LogicRuler;
import org.eclipse.gef.examples.logicdesigner.palette.LogicPaletteCustomizer;
import org.eclipse.gef.examples.logicdesigner.rulers.LogicRulerProvider;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.rulers.RulerProvider;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.AlignmentAction;
import org.eclipse.gef.ui.actions.CopyTemplateAction;
import org.eclipse.gef.ui.actions.DirectEditAction;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.MatchHeightAction;
import org.eclipse.gef.ui.actions.MatchWidthAction;
import org.eclipse.gef.ui.actions.ToggleGridAction;
import org.eclipse.gef.ui.actions.ToggleRulerVisibilityAction;
import org.eclipse.gef.ui.actions.ToggleSnapToGeometryAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.palette.PaletteViewer;
import org.eclipse.gef.ui.palette.PaletteViewerProvider;
import org.eclipse.gef.ui.parts.ContentOutlinePage;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.gef.ui.rulers.RulerComposite;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

public class LogicEditor
    extends GraphicalEditorWithFlyoutPalette
{

class OutlinePage
    extends ContentOutlinePage
    implements IAdaptable
{

    private PageBook pageBook;
    private Control outline;
    private Canvas overview;
    private IAction showOutlineAction, showOverviewAction;
    static final int ID_OUTLINE  = 0;
    static final int ID_OVERVIEW = 1;
    private Thumbnail thumbnail;
    private DisposeListener disposeListener;

    public OutlinePage(EditPartViewer viewer){
        super(viewer);
    }
    @Override
    public void init(IPageSite pageSite) {
        super.init(pageSite);
        ActionRegistry registry = LogicEditor.this.getActionRegistry();
        IActionBars bars = pageSite.getActionBars();
        String id = ActionFactory.UNDO.getId();
        bars.setGlobalActionHandler(id, registry.getAction(id));
        id = ActionFactory.REDO.getId();
        bars.setGlobalActionHandler(id, registry.getAction(id));
        id = ActionFactory.DELETE.getId();
        bars.setGlobalActionHandler(id, registry.getAction(id));
        id = IncrementDecrementAction.INCREMENT;
        bars.setGlobalActionHandler(id, registry.getAction(id));
        id = IncrementDecrementAction.DECREMENT;
        bars.setGlobalActionHandler(id, registry.getAction(id));
        bars.updateActionBars();
    }

    protected void configureOutlineViewer(){
        this.getViewer().setEditDomain(LogicEditor.this.getEditDomain());
        this.getViewer().setEditPartFactory(new TreePartFactory());
        ContextMenuProvider provider = new LogicContextMenuProvider(this.getViewer(), LogicEditor.this.getActionRegistry());
        this.getViewer().setContextMenu(provider);
        this.getSite().registerContextMenu(
            "org.eclipse.gef.examples.logic.outline.contextmenu", //$NON-NLS-1$
            provider, this.getSite().getSelectionProvider());
        this.getViewer().setKeyHandler(LogicEditor.this.getCommonKeyHandler());
        this.getViewer().addDropTargetListener((TransferDropTargetListener)
            new TemplateTransferDropTargetListener(this.getViewer()));
        IToolBarManager tbm = this.getSite().getActionBars().getToolBarManager();
        this.showOutlineAction = new Action() {
            @Override
            public void run() {
                OutlinePage.this.showPage(ID_OUTLINE);
            }
        };
        this.showOutlineAction.setImageDescriptor(ImageDescriptor.createFromFile(
                                LogicPlugin.class,"icons/outline.gif")); //$NON-NLS-1$
        this.showOutlineAction.setToolTipText(LogicMessages.LogicEditor_outline_show_outline);
        tbm.add(this.showOutlineAction);
        this.showOverviewAction = new Action() {
            @Override
            public void run() {
                OutlinePage.this.showPage(ID_OVERVIEW);
            }
        };
        this.showOverviewAction.setImageDescriptor(ImageDescriptor.createFromFile(
                                LogicPlugin.class,"icons/overview.gif")); //$NON-NLS-1$
        this.showOverviewAction.setToolTipText(LogicMessages.LogicEditor_outline_show_overview);
        tbm.add(this.showOverviewAction);
        this.showPage(ID_OUTLINE);
    }

    @Override
    public void createControl(Composite parent){
        this.pageBook = new PageBook(parent, SWT.NONE);
        this.outline = this.getViewer().createControl(this.pageBook);
        this.overview = new Canvas(this.pageBook, SWT.NONE);
        this.pageBook.showPage(this.outline);
        this.configureOutlineViewer();
        this.hookOutlineViewer();
        this.initializeOutlineViewer();
    }

    @Override
    public void dispose(){
        this.unhookOutlineViewer();
        if (this.thumbnail != null) {
            this.thumbnail.deactivate();
            this.thumbnail = null;
        }
        super.dispose();
        LogicEditor.this.outlinePage = null;
        LogicEditor.this.outlinePage = null;
    }

    public Object getAdapter(Class type) {
        if (type == ZoomManager.class) {
            return LogicEditor.this.getGraphicalViewer().getProperty(ZoomManager.class.toString());
        }
        return null;
    }

    @Override
    public Control getControl() {
        return this.pageBook;
    }

    protected void hookOutlineViewer(){
        LogicEditor.this.getSelectionSynchronizer().addViewer(this.getViewer());
    }

    protected void initializeOutlineViewer(){
        this.setContents(LogicEditor.this.getLogicDiagram());
    }

    protected void initializeOverview() {
        LightweightSystem lws = new LightweightSystem(this.overview);
        RootEditPart rep = LogicEditor.this.getGraphicalViewer().getRootEditPart();
        if (rep instanceof ScalableFreeformRootEditPart) {
            ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart)rep;
            this.thumbnail = new ScrollableThumbnail((Viewport)root.getFigure());
            this.thumbnail.setBorder(new MarginBorder(3));
            this.thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
            lws.setContents(this.thumbnail);
            this.disposeListener = new DisposeListener() {
                public void widgetDisposed(DisposeEvent e) {
                    if (OutlinePage.this.thumbnail != null) {
                        OutlinePage.this.thumbnail.deactivate();
                        OutlinePage.this.thumbnail = null;
                    }
                }
            };
            LogicEditor.this.getEditor().addDisposeListener(this.disposeListener);
        }
    }

    public void setContents(Object contents) {
        this.getViewer().setContents(contents);
    }

    protected void showPage(int id) {
        if (id == ID_OUTLINE) {
            this.showOutlineAction.setChecked(true);
            this.showOverviewAction.setChecked(false);
            this.pageBook.showPage(this.outline);
            if (this.thumbnail != null) {
                this.thumbnail.setVisible(false);
            }
        } else if (id == ID_OVERVIEW) {
            if (this.thumbnail == null) {
                this.initializeOverview();
            }
            this.showOutlineAction.setChecked(false);
            this.showOverviewAction.setChecked(true);
            this.pageBook.showPage(this.overview);
            this.thumbnail.setVisible(true);
        }
    }

    protected void unhookOutlineViewer(){
        LogicEditor.this.getSelectionSynchronizer().removeViewer(this.getViewer());
        if (this.disposeListener != null && LogicEditor.this.getEditor() != null && !LogicEditor.this.getEditor().isDisposed()) {
            LogicEditor.this.getEditor().removeDisposeListener(this.disposeListener);
        }
    }
}

private KeyHandler sharedKeyHandler;
private PaletteRoot root;
private OutlinePage outlinePage;
private boolean editorSaving = false;

// This class listens to changes to the file system in the workspace, and
// makes changes accordingly.
// 1) An open, saved file gets deleted -> close the editor
// 2) An open file gets renamed or moved -> change the editor's input accordingly
class ResourceTracker
    implements IResourceChangeListener, IResourceDeltaVisitor
{
    public void resourceChanged(IResourceChangeEvent event) {
        IResourceDelta delta = event.getDelta();
        try {
            if (delta != null) {
                delta.accept(this);
            }
        }
        catch (CoreException exception) {
            // What should be done here?
        }
    }
    public boolean visit(IResourceDelta delta) {
        if (delta == null || !delta.getResource().equals(((IFileEditorInput)LogicEditor.this.getEditorInput()).getFile())) {
            return true;
        }

        if (delta.getKind() == IResourceDelta.REMOVED) {
            Display display = LogicEditor.this.getSite().getShell().getDisplay();
            if ((IResourceDelta.MOVED_TO & delta.getFlags()) == 0) { // if the file was deleted
                // NOTE: The case where an open, unsaved file is deleted is being handled by the
                // PartListener added to the Workbench in the initialize() method.
                display.asyncExec(new Runnable() {
                    public void run() {
                        if (!LogicEditor.this.isDirty()) {
                            LogicEditor.this.closeEditor(false);
                        }
                    }
                });
            } else { // else if it was moved or renamed
                final IFile newFile = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getMovedToPath());
                display.asyncExec(new Runnable() {
                    public void run() {
                        LogicEditor.this.superSetInput(new FileEditorInput(newFile));
                    }
                });
            }
        } else if (delta.getKind() == IResourceDelta.CHANGED) {
            if (!LogicEditor.this.editorSaving) {
                // the file was overwritten somehow (could have been replaced by another
                // version in the respository)
                final IFile newFile = ResourcesPlugin.getWorkspace().getRoot()
                        .getFile(delta.getFullPath());
                Display display = LogicEditor.this.getSite().getShell().getDisplay();
                display.asyncExec(new Runnable() {
                    public void run() {
                        LogicEditor.this.setInput(new FileEditorInput(newFile));
                        LogicEditor.this.getCommandStack().flush();
                    }
                });
            }
        }
        return false;
    }
}

private IPartListener partListener = new IPartListener() {
    // If an open, unsaved file was deleted, query the user to either do a "Save As"
    // or close the editor.
    public void partActivated(IWorkbenchPart part) {
        if (part != LogicEditor.this) {
            return;
        }
        if (!((IFileEditorInput)LogicEditor.this.getEditorInput()).getFile().exists()) {
            Shell shell = LogicEditor.this.getSite().getShell();
            String title = LogicMessages.GraphicalEditor_FILE_DELETED_TITLE_UI;
            String message = LogicMessages.GraphicalEditor_FILE_DELETED_WITHOUT_SAVE_INFO;
            String[] buttons = { 	LogicMessages.GraphicalEditor_SAVE_BUTTON_UI,
                                       LogicMessages.GraphicalEditor_CLOSE_BUTTON_UI };
            MessageDialog dialog = new MessageDialog(
                    shell, title, null, message, MessageDialog.QUESTION, buttons, 0);
            if (dialog.open() == 0) {
                if (!LogicEditor.this.performSaveAs()) {
                    this.partActivated(part);
                }
            }
            else {
                LogicEditor.this.closeEditor(false);
            }
        }
    }
    public void partBroughtToTop(IWorkbenchPart part) {}
    public void partClosed(IWorkbenchPart part) {}
    public void partDeactivated(IWorkbenchPart part) {}
    public void partOpened(IWorkbenchPart part) {}
};

private LogicDiagram logicDiagram = new LogicDiagram();
private ResourceTracker resourceListener = new ResourceTracker();
private RulerComposite rulerComp;

protected static final String PALETTE_DOCK_LOCATION = "Dock location"; //$NON-NLS-1$
protected static final String PALETTE_SIZE = "Palette Size"; //$NON-NLS-1$
protected static final String PALETTE_STATE = "Palette state"; //$NON-NLS-1$
protected static final int DEFAULT_PALETTE_SIZE = 130;

static {
    LogicPlugin.getDefault().getPreferenceStore().setDefault(
            PALETTE_SIZE, DEFAULT_PALETTE_SIZE);
}

public LogicEditor() {
    this.setEditDomain(new DefaultEditDomain(this));
}

protected void closeEditor(boolean save) {
    this.getSite().getPage().closeEditor(LogicEditor.this, save);
}

@Override
public void commandStackChanged(EventObject event) {
    this.firePropertyChange(IEditorPart.PROP_DIRTY);
    super.commandStackChanged(event);
}

@Override
protected void configureGraphicalViewer() {
    super.configureGraphicalViewer();
    ScrollingGraphicalViewer viewer = (ScrollingGraphicalViewer)this.getGraphicalViewer();

    ScalableFreeformRootEditPart root = new ScalableFreeformRootEditPart();

    List zoomLevels = new ArrayList(3);
    zoomLevels.add(ZoomManager.FIT_ALL);
    zoomLevels.add(ZoomManager.FIT_WIDTH);
    zoomLevels.add(ZoomManager.FIT_HEIGHT);
    root.getZoomManager().setZoomLevelContributions(zoomLevels);

    IAction zoomIn = new ZoomInAction(root.getZoomManager());
    IAction zoomOut = new ZoomOutAction(root.getZoomManager());
    this.getActionRegistry().registerAction(zoomIn);
    this.getActionRegistry().registerAction(zoomOut);
    this.getSite().getKeyBindingService().registerAction(zoomIn);
    this.getSite().getKeyBindingService().registerAction(zoomOut);

    viewer.setRootEditPart(root);

    viewer.setEditPartFactory(new GraphicalPartFactory());
    ContextMenuProvider provider = new LogicContextMenuProvider(viewer, this.getActionRegistry());
    viewer.setContextMenu(provider);
    this.getSite().registerContextMenu("org.eclipse.gef.examples.logic.editor.contextmenu", //$NON-NLS-1$
            provider, viewer);
    viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer)
            .setParent(this.getCommonKeyHandler()));

    this.loadProperties();

    // Actions
    IAction showRulers = new ToggleRulerVisibilityAction(this.getGraphicalViewer());
    this.getActionRegistry().registerAction(showRulers);

    IAction snapAction = new ToggleSnapToGeometryAction(this.getGraphicalViewer());
    this.getActionRegistry().registerAction(snapAction);

    IAction showGrid = new ToggleGridAction(this.getGraphicalViewer());
    this.getActionRegistry().registerAction(showGrid);

    Listener listener = new Listener() {
        public void handleEvent(Event event) {
            LogicEditor.this.handleActivationChanged(event);
        }
    };
    this.getGraphicalControl().addListener(SWT.Activate, listener);
    this.getGraphicalControl().addListener(SWT.Deactivate, listener);
}

protected void writeToOutputStream(OutputStream os)throws IOException {
    ObjectOutputStream out = new ObjectOutputStream(os);
    out.writeObject(this.getLogicDiagram());
    out.close();
}

@Override
protected CustomPalettePage createPalettePage() {
    return new CustomPalettePage(this.getPaletteViewerProvider()) {
        @Override
        public void init(IPageSite pageSite) {
            super.init(pageSite);
            IAction copy = LogicEditor.this.getActionRegistry().getAction(ActionFactory.COPY.getId());
            pageSite.getActionBars().setGlobalActionHandler(
                    ActionFactory.COPY.getId(), copy);
        }
    };
}

@Override
protected PaletteViewerProvider createPaletteViewerProvider() {
    return new PaletteViewerProvider(this.getEditDomain()) {
        private IMenuListener menuListener;
        @Override
        protected void configurePaletteViewer(PaletteViewer viewer) {
            super.configurePaletteViewer(viewer);
            viewer.setCustomizer(new LogicPaletteCustomizer());
            viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer));
        }
        @Override
        protected void hookPaletteViewer(PaletteViewer viewer) {
            super.hookPaletteViewer(viewer);
            final CopyTemplateAction copy = (CopyTemplateAction)LogicEditor.this.getActionRegistry()
                    .getAction(ActionFactory.COPY.getId());
            viewer.addSelectionChangedListener(copy);
            if (this.menuListener == null) {
                this.menuListener = new IMenuListener() {
                    public void menuAboutToShow(IMenuManager manager) {
                        manager.appendToGroup(GEFActionConstants.GROUP_COPY, copy);
                    }
                };
            }
            viewer.getContextMenu().addMenuListener(this.menuListener);
        }
    };
}

@Override
public void dispose() {
    this.getSite().getWorkbenchWindow().getPartService().removePartListener(this.partListener);
    this.partListener = null;
    ((IFileEditorInput)this.getEditorInput()).getFile().getWorkspace()
            .removeResourceChangeListener(this.resourceListener);
    super.dispose();
}

@Override
public void doSave(final IProgressMonitor progressMonitor) {
    this.editorSaving = true;
    Platform.run(new SafeRunnable() {
        public void run() throws Exception {
            LogicEditor.this.saveProperties();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            LogicEditor.this.writeToOutputStream(out);
            IFile file = ((IFileEditorInput)LogicEditor.this.getEditorInput()).getFile();
            file.setContents(new ByteArrayInputStream(out.toByteArray()),
                            true, false, progressMonitor);
            LogicEditor.this.getCommandStack().markSaveLocation();
        }
    });
    this.editorSaving = false;
}

@Override
public void doSaveAs() {
    this.performSaveAs();
}

@Override
public Object getAdapter(Class type){
    if (type == IContentOutlinePage.class) {
        this.outlinePage = new OutlinePage(new TreeViewer());
        return this.outlinePage;
    }
    if (type == ZoomManager.class) {
        return this.getGraphicalViewer().getProperty(ZoomManager.class.toString());
    }

    Object object = super.getAdapter(type);

    return object;
}

@Override
protected Control getGraphicalControl() {
    return this.rulerComp;
}

/**
 * Returns the KeyHandler with common bindings for both the Outline and Graphical Views.
 * For example, delete is a common action.
 */
protected KeyHandler getCommonKeyHandler(){
    if (this.sharedKeyHandler == null){
        this.sharedKeyHandler = new KeyHandler();
        this.sharedKeyHandler.put(
            KeyStroke.getPressed(SWT.F2, 0),
            this.getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
    }
    return this.sharedKeyHandler;
}

protected LogicDiagram getLogicDiagram() {
    return this.logicDiagram;
}

@Override
protected PaletteRoot getPaletteRoot() {
    if( this.root == null ){
        this.root = LogicPlugin.createPalette();
    }
    return this.root;
}

public void gotoMarker(IMarker marker) {}

protected void handleActivationChanged(Event event) {
    IAction copy = null;
    if (event.type == SWT.Deactivate) {
        copy = this.getActionRegistry().getAction(ActionFactory.COPY.getId());
    }
    if (this.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.COPY.getId())
            != copy) {
        this.getEditorSite().getActionBars().setGlobalActionHandler(
                ActionFactory.COPY.getId(), copy);
        this.getEditorSite().getActionBars().updateActionBars();
    }
}

@Override
protected void initializeGraphicalViewer() {
    super.initializeGraphicalViewer();
    this.getGraphicalViewer().setContents(this.getLogicDiagram());

    this.getGraphicalViewer().addDropTargetListener((TransferDropTargetListener)
        new TemplateTransferDropTargetListener(this.getGraphicalViewer()));
    this.getGraphicalViewer().addDropTargetListener((TransferDropTargetListener)
        new TextTransferDropTargetListener(
                this.getGraphicalViewer(), TextTransfer.getInstance()));
}

@Override
protected void createActions() {
    super.createActions();
    ActionRegistry registry = this.getActionRegistry();
    IAction action;

    action = new CopyTemplateAction(this);
    registry.registerAction(action);

    action = new MatchWidthAction(this);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new MatchHeightAction(this);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new LogicPasteTemplateAction(this);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new IncrementDecrementAction(this, true);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new IncrementDecrementAction(this, false);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new DirectEditAction((IWorkbenchPart)this);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.LEFT);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.RIGHT);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.TOP);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.BOTTOM);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.CENTER);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());

    action = new AlignmentAction((IWorkbenchPart)this, PositionConstants.MIDDLE);
    registry.registerAction(action);
    this.getSelectionActions().add(action.getId());
}

/* (non-Javadoc)
 * @see org.eclipse.gef.ui.parts.GraphicalEditor#createGraphicalViewer(org.eclipse.swt.widgets.Composite)
 */
@Override
protected void createGraphicalViewer(Composite parent) {
    this.rulerComp = new RulerComposite(parent, SWT.NONE);
    super.createGraphicalViewer(this.rulerComp);
    this.rulerComp.setGraphicalViewer((ScrollingGraphicalViewer)this.getGraphicalViewer());
}

protected FigureCanvas getEditor(){
    return (FigureCanvas)this.getGraphicalViewer().getControl();
}

@Override
public boolean isSaveAsAllowed() {
    return true;
}

protected void loadProperties() {
    // Ruler properties
    LogicRuler ruler = this.getLogicDiagram().getRuler(PositionConstants.WEST);
    RulerProvider provider = null;
    if (ruler != null) {
        provider = new LogicRulerProvider(ruler);
    }
    this.getGraphicalViewer().setProperty(RulerProvider.PROPERTY_VERTICAL_RULER, provider);
    ruler = this.getLogicDiagram().getRuler(PositionConstants.NORTH);
    provider = null;
    if (ruler != null) {
        provider = new LogicRulerProvider(ruler);
    }
    this.getGraphicalViewer().setProperty(RulerProvider.PROPERTY_HORIZONTAL_RULER, provider);
    this.getGraphicalViewer().setProperty(RulerProvider.PROPERTY_RULER_VISIBILITY,
            new Boolean(this.getLogicDiagram().getRulerVisibility()));

    // Snap to Geometry property
    this.getGraphicalViewer().setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED,
            new Boolean(this.getLogicDiagram().isSnapToGeometryEnabled()));

    // Grid properties
    this.getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_ENABLED,
            new Boolean(this.getLogicDiagram().isGridEnabled()));
    // We keep grid visibility and enablement in sync
    this.getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE,
            new Boolean(this.getLogicDiagram().isGridEnabled()));

    // Zoom
    ZoomManager manager = (ZoomManager)this.getGraphicalViewer()
            .getProperty(ZoomManager.class.toString());
    if (manager != null) {
        manager.setZoom(this.getLogicDiagram().getZoom());
    }
    // Scroll-wheel Zoom
    this.getGraphicalViewer().setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1),
            MouseWheelZoomHandler.SINGLETON);

}

protected boolean performSaveAs() {
    SaveAsDialog dialog = new SaveAsDialog(this.getSite().getWorkbenchWindow().getShell());
    dialog.setOriginalFile(((IFileEditorInput)this.getEditorInput()).getFile());
    dialog.open();
    IPath path= dialog.getResult();

    if (path == null) {
        return false;
    }

    IWorkspace workspace = ResourcesPlugin.getWorkspace();
    final IFile file= workspace.getRoot().getFile(path);

    if (!file.exists()) {
        WorkspaceModifyOperation op= new WorkspaceModifyOperation() {
            @Override
            public void execute(final IProgressMonitor monitor) {
                LogicEditor.this.saveProperties();
                try {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    LogicEditor.this.writeToOutputStream(out);
                    file.create(new ByteArrayInputStream(out.toByteArray()), true, monitor);
                    out.close();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        try {
            new ProgressMonitorDialog(this.getSite().getWorkbenchWindow().getShell())
                    .run(false, true, op);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    try {
        this.superSetInput(new FileEditorInput(file));
        this.getCommandStack().markSaveLocation();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}

protected void saveProperties() {
    this.getLogicDiagram().setRulerVisibility(((Boolean)this.getGraphicalViewer()
            .getProperty(RulerProvider.PROPERTY_RULER_VISIBILITY)).booleanValue());
    this.getLogicDiagram().setGridEnabled(((Boolean)this.getGraphicalViewer()
            .getProperty(SnapToGrid.PROPERTY_GRID_ENABLED)).booleanValue());
    this.getLogicDiagram().setSnapToGeometry(((Boolean)this.getGraphicalViewer()
            .getProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED)).booleanValue());
    ZoomManager manager = (ZoomManager)this.getGraphicalViewer()
            .getProperty(ZoomManager.class.toString());
    if (manager != null) {
        this.getLogicDiagram().setZoom(manager.getZoom());
    }
}

@Override
protected void setInput(IEditorInput input) {
    this.superSetInput(input);

    IFile file = ((IFileEditorInput)input).getFile();
    try {
        InputStream is = file.getContents(false);
        ObjectInputStream ois = new ObjectInputStream(is);
        this.setLogicDiagram((LogicDiagram)ois.readObject());
        ois.close();
    }
    catch (Exception e) {
        //This is just an example.  All exceptions caught here.
        e.printStackTrace();
    }

    if (!this.editorSaving) {
        if (this.getGraphicalViewer() != null) {
            this.getGraphicalViewer().setContents(this.getLogicDiagram());
            this.loadProperties();
        }
        if (this.outlinePage != null) {
            this.outlinePage.setContents(this.getLogicDiagram());
        }
    }
}

public void setLogicDiagram(LogicDiagram diagram) {
    this.logicDiagram = diagram;
}

protected void superSetInput(IEditorInput input) {
    // The workspace never changes for an editor.  So, removing and re-adding the
    // resourceListener is not necessary.  But it is being done here for the sake
    // of proper implementation.  Plus, the resourceListener needs to be added
    // to the workspace the first time around.
    if(this.getEditorInput() != null) {
        IFile file = ((IFileEditorInput)this.getEditorInput()).getFile();
        file.getWorkspace().removeResourceChangeListener(this.resourceListener);
    }

    super.setInput(input);

    if(this.getEditorInput() != null) {
        IFile file = ((IFileEditorInput)this.getEditorInput()).getFile();
        file.getWorkspace().addResourceChangeListener(this.resourceListener);
        this.setPartName(file.getName());
    }
}

@Override
protected void setSite(IWorkbenchPartSite site){
    super.setSite(site);
    this.getSite().getWorkbenchWindow().getPartService().addPartListener(this.partListener);
}

}
